Conversation
Add building blocks for creating BOLT12 invoices with blinded payment paths through an LSP using intercept SCIDs, enabling LSPS2 JIT channel opens for BOLT12 payments. - Add `create_blinded_payment_paths_for_intercept_scid()` on `OffersMessageFlow` to construct blinded payment paths through the LSP using LSPS2 parameters (intercept SCID, CLTV delta, zero fees) - Add `create_invoice_builder_from_invoice_request_with_custom_payment_paths()` on `OffersMessageFlow` to build BOLT12 invoices with pre-built payment paths - Add `manually_handle_bolt12_invoice_requests` config option and `Event::InvoiceRequestReceived` to allow external handling of invoice requests (needed for LSPS2 clients that must construct custom paths) - Add `send_invoice_for_request()` and `offers_handler()` on `ChannelManager` for sending back manually-constructed invoices - Update `InvoiceParametersReady` docs for BOLT12 usage Co-Authored-By: HAL 9000
Add `register_peer_for_interception()` and `deregister_peer_for_interception()` methods to `OnionMessenger`, allowing specific peers to be registered for onion message interception without enabling blanket interception for all offline peers. When a registered peer is offline and an onion message needs to be forwarded to them, `Event::OnionMessageIntercepted` is emitted. When a registered peer connects, `Event::OnionMessagePeerConnected` is emitted. This works alongside the existing global `new_with_offline_peer_interception()` flag — if either the global flag is set or the peer is specifically registered, interception occurs. This enables LSPS2 services to intercept onion messages only for peers with active JIT channel sessions, rather than intercepting messages for all offline peers. Co-Authored-By: HAL 9000
Define the `OnionMessageInterceptor` trait with `register_peer_for_interception()` and `deregister_peer_for_interception()` methods, and implement it for `OnionMessenger`. This allows external components to register peers for onion message interception via a trait object, without needing to know the concrete `OnionMessenger` type. Wire the trait into `LSPS2ServiceHandler` as an optional `Arc<dyn OnionMessageInterceptor>`. When provided: - On init, all peers with active intercept SCIDs are registered - In `invoice_parameters_generated()`, the counterparty is registered when a new intercept SCID is assigned This ensures that onion messages for LSPS2 clients with active JIT channel sessions are intercepted when those clients are offline, enabling the LSP to store and forward messages when the client reconnects. Co-Authored-By: HAL 9000
When intercept SCIDs are removed during cleanup, also clean up the handler-level `peer_by_intercept_scid` map and deregister the peer from onion message interception if they have no remaining active SCIDs. Cleanup is added in all relevant paths: - `prune_expired_request_state()` now returns pruned SCIDs - `peer_disconnected()` cleans up after pruning - `htlc_intercepted()` error path (fixes existing TODO) - `channel_open_abandoned()` cleans up after removing the SCID - `persist()` cleans up both `peer_by_intercept_scid` and `peer_by_channel_id` when removing a peer entry entirely Co-Authored-By: HAL 9000
|
👋 Hi! This PR is now in draft status. |
Add a convenience method on `ChannelManager` that combines all the steps needed to create and send a BOLT12 invoice using LSPS2 JIT channel parameters: 1. Verify the invoice request 2. Create an inbound payment (payment hash + secret) 3. Build blinded payment paths through the LSP using the intercept SCID 4. Construct the BOLT12 invoice with those paths 5. Send the invoice back via the responder This simplifies the LSPS2 client flow: instead of manually calling five separate methods across `OffersMessageFlow` and `ChannelManager`, the caller can handle `Event::InvoiceRequestReceived` with a single call, passing the LSPS2 parameters from `InvoiceParametersReady`. Co-Authored-By: HAL 9000
…aths Add a test that verifies the `manually_handle_bolt12_invoice_requests` config flag and `send_bolt12_invoice_for_intercept_scid` convenience method work correctly together. The test exercises the full LSPS2+BOLT12 flow: creating an offer, receiving an `InvoiceRequest` via the `Event::InvoiceRequestReceived` event, building a BOLT12 invoice with blinded payment paths using an intercept SCID, and sending the invoice back to the payer via onion message. Co-Authored-By: HAL 9000
Add `bolt12_lsps2_end_to_end_test` that exercises the full BOLT12 + LSPS2 JIT channel lifecycle across three nodes (payer, service/LSP, client): - LSPS2 ceremony to negotiate JIT channel parameters - Client creates BOLT12 offer with `manually_handle_bolt12_invoice_requests` - Payer sends `InvoiceRequest` routed through the service via onion messages - Client handles `InvoiceRequestReceived` and calls `send_bolt12_invoice_for_intercept_scid` to create an invoice with blinded payment paths through the service's intercept SCID - Payer pays the invoice through the blinded path - Service intercepts the HTLC and opens a JIT channel to the client - Client claims, service broadcasts the funding transaction Co-Authored-By: HAL 9000
2d9c5a0 to
c847f08
Compare
| /// [`OffersMessageFlow::create_blinded_payment_paths_for_intercept_scid`]: crate::offers::flow::OffersMessageFlow::create_blinded_payment_paths_for_intercept_scid | ||
| /// [`OffersMessageFlow::create_invoice_builder_from_invoice_request_with_custom_payment_paths`]: crate::offers::flow::OffersMessageFlow::create_invoice_builder_from_invoice_request_with_custom_payment_paths | ||
| /// [`ChannelManager::send_invoice_for_request`]: crate::ln::channelmanager::ChannelManager::send_invoice_for_request | ||
| InvoiceRequestReceived { |
There was a problem hiding this comment.
We've been discussing for removing these events for awhile now in favor of events in OffersMessageFlow. But maybe we've come full circle on that? See: #3833 (comment). (cc: @TheBlueMatt)
There's definitely a lot of handling logic in ChannelManager's implementation of OffersMessageHandler that wouldn't be great if a custom implementation needed to reproduce.
| /// [`Self::create_invoice_builder_from_invoice_request_with_custom_payment_paths`]. | ||
| /// | ||
| /// [`ChannelManager::create_inbound_payment`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment | ||
| pub fn create_blinded_payment_paths_for_intercept_scid<ES: EntropySource>( |
There was a problem hiding this comment.
Should this be a custom Router implementation instead? For message blinded paths, we took the approach of allowing to pass a custom MessageRouter if we want non-standard blinded path creation.
| /// respond to the counterparty. | ||
| /// | ||
| /// [`ChannelManager::create_inbound_payment`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment | ||
| pub fn create_invoice_builder_from_invoice_request_with_custom_payment_paths<'a>( |
There was a problem hiding this comment.
Then this wouldn't be needed since you'd just call create_invoice_builder_from_invoice_request_without_keys with a custom Router.
Finally adding BOLT12 support to the LSPS2 flow.
For now, this is just a draft as basis for further discussion on the approach. (@jkczyz)